#include "tile.h"
#include <stdlib.h>
#include <assert.h>
#include "util/cbuf.h"

#define TILE_EXT_BLOCK_SIZE 256

typedef struct {
	TileExtension* extension;
	int n_extension;
} ExtBlock;

static Cbuf ext_block;

static TileExtension* next_extension ();

void tile_init ()
{
	cbuf_init (&ext_block, sizeof(ExtBlock), 1);
	ExtBlock first;
	first.extension = malloc(TILE_EXT_BLOCK_SIZE * sizeof(TileExtension));
	first.n_extension = 0;
	cbuf_push (&ext_block, &first);
}

void tile_cleanup ()
{
	for (int i = 0; i < ext_block.count; i++) {
		ExtBlock* block = CBUF_ELEM(&ext_block,i,ExtBlock);
		free (block->extension);
	}
	cbuf_cleanup (&ext_block);
}

void tile_make_floor (Tile* tile)
{
	assert(tile != NULL);
	assert(tile->extension == NULL); // must be virgin tile
	
	tile->type = TILE_COMMON;
	
	tile->blocks_players = false;
	tile->blocks_monsters = false;
	tile->blocks_ghosts = false;
	tile->blocks_objects = false;
	
	tile->is_room = false;
	tile->has_actor = false;
	tile->has_object = false;
	tile->object = NULL;
	
	tile->extension = NULL;
}

void tile_make_wall (Tile* tile)
{
	assert(tile != NULL);
	assert(tile->extension == NULL); // must be virgin tile
	
	tile->type = TILE_COMMON;
	
	tile->blocks_players = true;
	tile->blocks_monsters = true;
	tile->blocks_ghosts = true;
	tile->blocks_objects = true;
	
	tile->is_room = false;
	tile->has_actor = false;
	tile->has_object = false;
	tile->object = NULL;
	
	tile->extension = NULL;
}

void tile_make_spawn (Tile* tile, int index)
{
	assert(tile != NULL);
	assert(tile->extension == NULL); // must be virgin tile
	
	tile->type = TILE_EXTENDED;
	
	tile->blocks_players = false;
	tile->blocks_monsters = true;
	tile->blocks_ghosts = true;
	tile->blocks_objects = true;
	
	tile->is_room = true;
	tile->has_actor = false;
	tile->has_object = false;
	tile->object = NULL;
	
	tile->extension = next_extension ();
	tile->extension->type = TE_SPAWN;
	tile->extension->spawn.index = index;
}

void tile_make_teleporter (Tile* tile, Coord target)
{
	assert(tile != NULL);
	assert(tile->extension == NULL); // must be virgin tile
	
	tile->type = TILE_EXTENDED;
	
	tile->blocks_players = false;
	tile->blocks_monsters = true;
	tile->blocks_ghosts = false;
	tile->blocks_objects = true;
	
	tile->is_room = false;
	tile->has_actor = false;
	tile->has_object = false;
	tile->object = NULL;
	
	tile->extension = next_extension ();
	tile->extension->type = TE_TELEPORTER;
	tile->extension->teleporter.target = target;
}

void tile_make_stairs (Tile* tile, Direction dir, Coord target)
{
	assert(tile != NULL);
	assert(tile->extension == NULL); // must be virgin tile
	
	tile->type = TILE_EXTENDED;
	
	tile->blocks_players = false;
	tile->blocks_monsters = true;
	tile->blocks_ghosts = false;
	tile->blocks_objects = true;
	
	tile->is_room = false;
	tile->has_actor = false;
	tile->has_object = false;
	tile->object = NULL;
	
	tile->extension = next_extension ();
	tile->extension->type = TE_STAIRS;
	tile->extension->stairs.dir = dir;
	tile->extension->stairs.target = target;
}

static TileExtension* next_extension ()
{
	assert(ext_block.count > 0);
	assert(TILE_EXT_BLOCK_SIZE > 0);
	
	ExtBlock* block = CBUF_HEAD(&ext_block,ExtBlock);
	
	if (block->n_extension >= TILE_EXT_BLOCK_SIZE) {
		ExtBlock new_block;
		new_block.extension = malloc(TILE_EXT_BLOCK_SIZE * sizeof(TileExtension));
		new_block.n_extension = 0;
		cbuf_push (&ext_block, &new_block);
		block = CBUF_HEAD(&ext_block,ExtBlock);
	}
	
	return &block->extension[block->n_extension++];
}
